/*******************************************************************************
** Copyright (c) 2004 MaK Technologies, Inc.
** All rights reserved.
*******************************************************************************/
/*******************************************************************************
** $RCSfile: simple1516.cxx,v $ $Revision: 1.6 $ $State: Exp $
*******************************************************************************/

// A simple federate that updates an object with attributes whose values
// are the name of the attribute. Objects from other simple federates
// are discovered and reflected. The string values are byte encoded to allow
// compatiblity between 1.3 and 1516

#pragma warning(disable: 4251)
#pragma warning(disable: 4786)
#pragma warning(disable: 4290)

#ifdef WIN32
#include <winsock2.h>
#include <process.h>

#if (_MSC_VER == 1200)
// Test for XTREE patch in MSVC++ 6.0
#include <XTREE>
#ifdef _XTREE_
#error FATAL_ERROR: You must install the Dinkumware XTREE patch before using HLA 1516 with MSVC++ 6.0
#endif
#endif
#else
#include <unistd.h>
#endif

#include <stdio.h>
#include <wchar.h>
#include <sstream>
#include <string>
#include <iostream>
#include <sstream>

#include <RTI/RTI1516.h>
#include <RTI/RTIambassadorFactory.h>
#include <RTI/logicalTimeFactoryImpl.h>
#include "fedAmb1516.h"
#include "keyboard.h"
#include "stringUtil.h"

#include "pdasrv.h"

//pdasrv server;
int main3()
{

//	server.start(5000);
	return 0;
}

// Logical time factory implementation
LogicalTimeFactoryImpl theTimeFactory;

// Handles keyboard input without blocking
keyboard input;

using namespace rti1516;
using namespace std;

// Map between strings and attribute handles
typedef std::map<std::wstring, AttributeHandle>  DtAttrNameHandleMap;

// Map between strings and parameter handles
typedef std::map<std::wstring, ParameterHandle>  DtParamNameHandleMap;


// Data shared between federate and federate ambassador
DtTalkAmbData theAmbData;

// The object class name
std::wstring theClassName = L"BaseEntity";

// The interaction class name
std::wstring theInterClassName = L"WeaponFire";

// The object class handle (to be retrieved from RTI).
ObjectClassHandle theClassHandle;

// The interaction class handle (to be retrieved from RTI).
InteractionClassHandle theInterClassHandle;

// The object instance handle (to be retrieved from the RTI).
ObjectInstanceHandle theObjectHandle;

// Map between strings and attribute handles
DtAttrNameHandleMap theAttrNameHandleMap;

// Map between strings and parameter handles
DtParamNameHandleMap theParamNameHandleMap;

////////////////////////////////////////////////////////////////////////////////
// Create the federation execution
void createFedEx(RTIambassador & rtiAmb,
                 std::wstring const& fedName,
                 std::wstring const& fedFile)
{
   wcout << L"createFederationExecution "
         << fedName.c_str() << L" "
         << fedFile.c_str() << endl;
   try
   {
      rtiAmb.createFederationExecution(fedName, fedFile);
   }
   catch(FederationExecutionAlreadyExists& ex)
   {
      wcout << L"Could not create Federation Execution: "
            << L"FederationExecutionAlreadyExists: "
            << ex.what().c_str() << endl;
   }
   catch(rti1516::Exception& ex)
   {
      wcout << L"RTI Exception: "
            << ex.what().c_str() << endl
            << L"Could not create Federation Execution: " << endl;
      exit(0);
   }
   rtiAmb.evokeMultipleCallbacks(0.1, 0.2);

   wcout << L"Federation Created." << endl;
}

////////////////////////////////////////////////////////////////////////////////
// Join the federation execution
void joinFedEx(
   RTIambassador & rtiAmb, MyFederateAmbassador & fedAmb,
   std::wstring const& federateType, std::wstring const& federationName)
{
   bool joined=false;
   const int maxTry  = 10;
   int numTries = 0;
   wcout << L"joinFederationExecution "
         << federateType.c_str() << L" "
         << federationName.c_str() << endl;

   while (!joined && numTries++ < maxTry)
   {
      try
      {
         rtiAmb.joinFederationExecution(federateType, federationName, fedAmb);
         joined = true;
      }
      catch(FederationExecutionDoesNotExist)
      {
         wcout << L"FederationExecutionDoesNotExist, try "
               << numTries << L" out of "
               << maxTry << endl;
         continue;
      }
      catch(rti1516::Exception& ex)
      {
         wcout << L"RTI Exception: "
               << ex.what().c_str() << endl;
         return;
      }
      rtiAmb.evokeMultipleCallbacks(0.1, 0.2);
   }
   if (joined)
      wcout << L"Joined Federation." << endl;
   else
   {
      wcout << L"Giving up." << endl;
      rtiAmb.destroyFederationExecution(federationName);
      exit(0);
   }
}

////////////////////////////////////////////////////////////////////////////////
// Resign and destroy the federation execution
void resignAndDestroy( RTIambassador & rtiAmb,
      std::wstring const& federationName)
{
   rtiAmb.resignFederationExecution(rti1516::DELETE_OBJECTS);
   rtiAmb.destroyFederationExecution(federationName);
}

////////////////////////////////////////////////////////////////////////////////
// Publish and subscribe the object class attributes.
// Register an object instance of the class.
bool publishSubscribeAndRegisterObject(RTIambassador & rtiAmb)
{
   AttributeHandleSet hSet;
   // Get the object class handle
   try
   {  
      theClassHandle = rtiAmb.getObjectClassHandle(theClassName);
      theAmbData.objectClassMap[theClassHandle] = theClassName;
   }
   catch (rti1516::Exception& ex)
   {
      wcout << L"RTI Exception: "
            << ex.what().c_str() << endl;
      wcout << L"Could not get object class handle: "
            << theClassName.c_str() << endl;
      return false;
   }

   // Get the attribute handles and construct the name-handle map and
   // attribute set
   std::wstring attrName;
   try
   {
      attrName  = L"AccelerationVector";
      theAttrNameHandleMap[attrName] = 
            rtiAmb.getAttributeHandle(theClassHandle, attrName);
      hSet.insert(theAttrNameHandleMap[attrName]);
      attrName  = L"VelocityVector";
      theAttrNameHandleMap[attrName] = 
            rtiAmb.getAttributeHandle(theClassHandle, attrName);
      hSet.insert(theAttrNameHandleMap[attrName]);
      attrName  = L"Orientation";
      theAttrNameHandleMap[attrName] = 
            rtiAmb.getAttributeHandle(theClassHandle, attrName);
      hSet.insert(theAttrNameHandleMap[attrName]);
      attrName  = L"DeadReckoningAlgorithm";
      theAttrNameHandleMap[attrName] = 
            rtiAmb.getAttributeHandle(theClassHandle, attrName);
      hSet.insert(theAttrNameHandleMap[attrName]);
      attrName  = L"WorldLocation";
      theAttrNameHandleMap[attrName] = 
            rtiAmb.getAttributeHandle(theClassHandle, attrName);
      hSet.insert(theAttrNameHandleMap[attrName]);
   }
   catch (rti1516::Exception& ex)
   {
      wcout << L"RTI Exception: "
            << ex.what().c_str() << endl;
      wcout << L"Could not get attribute handle "
            << attrName.c_str() << endl;
      return false;
   }

    // Publish and subscribe
   int cnt=0;
   try
   {
      rtiAmb.publishObjectClassAttributes(theClassHandle, hSet);
      rtiAmb.evokeMultipleCallbacks(0.1, 0.2);
      cnt=1;
      rtiAmb.subscribeObjectClassAttributes(theClassHandle, hSet);
      rtiAmb.evokeMultipleCallbacks(0.1, 0.2);
   }
   catch (rti1516::Exception& ex)
   {
      wcout << L"RTI Exception: "
            << ex.what().c_str() << endl;
      wcout << L"Could not "
            << (cnt ? L"publish" : L"subscribe") << endl;
      return false;
   }

   std::wstring objectName(L"Talk");

   // Reserve object name and register the object instance
   try
   {
      unsigned int objId = abs(getpid());
      std::wstringstream pid;
      pid << objId;
      objectName += pid.str();
      theAmbData.myNameReservationReturned = 
      theAmbData.myNameReservationSucceeded = false;
      rtiAmb.reserveObjectInstanceName(objectName);
      int count = 0;
      while (count++ < 100 && !theAmbData.myNameReservationReturned)
      {
         rtiAmb.evokeMultipleCallbacks(0.1, 0.2);
      }

      if (count < 100)
      {
         theObjectHandle = rtiAmb.registerObjectInstance(theClassHandle,
               objectName);
         //theObjectHandle = rtiAmb.registerObjectInstance(theClassHandle);

         // Add name-handle to map
         theAmbData.objectInstanceMap[theObjectHandle] =
            rtiAmb.getObjectInstanceName(theObjectHandle);
      }
      else
      {
         wcout << L"Failed waiting for reserve object name "
               << objectName.c_str() << endl;
         return false;
      }
      rtiAmb.evokeMultipleCallbacks(0.1, 0.2);
   }
   catch (rti1516::Exception& ex)
   {
      wcout << L"RTI Exception: "
            << ex.what().c_str() << endl;
      wcout << L"Could not  Register Object "
            << objectName.c_str()
            << L" with class "
            << theClassName.c_str() << endl;
      return false;
   }

   wcout << L"Registered object "
         << objectName.c_str()
         << L" with class name "
         << theClassName.c_str() << endl;
   return true;
}

////////////////////////////////////////////////////////////////////////////////
// Publish and Subscribe to an interaction class
bool publishAndSubscribeInteraction(RTIambassador & rtiAmb)
{
   // Get the interaction class handle
   try
   {  
      theInterClassHandle = rtiAmb.getInteractionClassHandle(theInterClassName.c_str());
      theAmbData.interactionClassMap[theInterClassHandle] = theInterClassName;
   }
   catch (rti1516::Exception& ex)
   {
      wcout << L"RTI Exception: "
            << ex.what().c_str() << endl;
      wcout << L"Could not get interaction class handle: "
            << theClassName.c_str() << endl;
      return false;
   }

   // Get the parameter handles and construct the name-handle map
   std::wstring paramName;
   try
   {
      paramName  = L"EventIdentifier";
      theParamNameHandleMap[paramName] = 
            rtiAmb.getParameterHandle(theInterClassHandle, paramName);
      paramName  = L"FireControlSolutionRange";
      theParamNameHandleMap[paramName] = 
            rtiAmb.getParameterHandle(theInterClassHandle, paramName);
      paramName  = L"FireMissionIndex";
      theParamNameHandleMap[paramName] = 
            rtiAmb.getParameterHandle(theInterClassHandle, paramName);
      paramName  = L"FiringLocation";
      theParamNameHandleMap[paramName] = 
            rtiAmb.getParameterHandle(theInterClassHandle, paramName);
      paramName  = L"FiringObjectIdentifier";
      theParamNameHandleMap[paramName] = 
            rtiAmb.getParameterHandle(theInterClassHandle, paramName);
      paramName  = L"FuseType";
      theParamNameHandleMap[paramName] = 
            rtiAmb.getParameterHandle(theInterClassHandle, paramName);
      paramName  = L"InitialVelocityVector";
      theParamNameHandleMap[paramName] = 
            rtiAmb.getParameterHandle(theInterClassHandle, paramName);
      paramName  = L"MunitionObjectIdentifier";
      theParamNameHandleMap[paramName] = 
            rtiAmb.getParameterHandle(theInterClassHandle, paramName);
      paramName  = L"MunitionType";
      theParamNameHandleMap[paramName] = 
            rtiAmb.getParameterHandle(theInterClassHandle, paramName);
      paramName  = L"QuantityFired";
      theParamNameHandleMap[paramName] = 
            rtiAmb.getParameterHandle(theInterClassHandle, paramName);
      paramName  = L"RateOfFire";
      theParamNameHandleMap[paramName] = 
            rtiAmb.getParameterHandle(theInterClassHandle, paramName);
      paramName  = L"TargetObjectIdentifier";
      theParamNameHandleMap[paramName] = 
            rtiAmb.getParameterHandle(theInterClassHandle, paramName);
      paramName  = L"WarheadType";
      theParamNameHandleMap[paramName] = 
            rtiAmb.getParameterHandle(theInterClassHandle, paramName);
   }
   catch (rti1516::Exception& ex)
   {
      wcout << L"RTI Exception: "
            << ex.what().c_str() << endl;
      wcout << L"Could not get parameter handle "
            << paramName.c_str() << endl;
      return false;
   }

    // Publish and subscribe
   int cnt=0;
   try
   {
   rtiAmb.publishInteractionClass(theInterClassHandle);
   rtiAmb.evokeMultipleCallbacks(0.1, 0.2);
   cnt=1;
   rtiAmb.subscribeInteractionClass(theInterClassHandle);
   rtiAmb.evokeMultipleCallbacks(0.1, 0.2);
   }
   catch (rti1516::Exception& ex)
   {
      wcout << L"RTI Exception: "
            << ex.what().c_str() << endl;
      wcout << L"Could not "
            << (cnt ? L"publish" : L"subscribe")
            << L" to interaction class." << endl;
      return false;
   }

   wcout  << L"Subscribed to interaction class: "
          << theInterClassName.c_str()
          << L" with handle: "
          <<  theInterClassHandle.toString() << endl;
   return true;
}

// TODO start a socket and listen it, RTI commands should come to this socket
int main(int argc, char** argv)
{
   try
   {
      // Federate and Federation info
      std::vector< std::wstring > args;
      std::wstring federationName(L"MAKsimple");
      std::wstring federationFile(L"MAKsimple.xml");
      std::wstring federateType(L"rtisimple1516");

      if (argc > 1)
      {
         if (strcmp(argv[1], "-h") == 0)
         {
            wcout << L"Usage: talk1516 [federtionName] [federationFile] [federateType]" << endl;
            wcout << L"default values: "
                  << L"\"" << federationName.c_str() << L"\" "
                  << L"\"" << federationFile.c_str() <<  L"\" "
                  << L"\"" << federateType.c_str() <<  L"\" " << endl;
            return 0;
         }
         federationName = DtToWString(argv[1]);
      }

      if (argc > 2)
      {
         federationFile = DtToWString(argv[2]);
      }

      if (argc > 3)
      {
         federateType = DtToWString(argv[3]);
      }

      wcout << L"Using " << RTIname().c_str() << L" " << RTIversion().c_str() << L"\n";

      // RTI and Federate Ambassadors
      RTIambassadorFactory* rtiAmbFactory = new RTIambassadorFactory();
      std::auto_ptr < RTIambassador > rtiAmbAP =
         rtiAmbFactory->createRTIambassador(args);
      RTIambassador* rtiAmb = rtiAmbAP.release();
      MyFederateAmbassador fedAmb(theAmbData);

      // Create the federation
      createFedEx(*rtiAmb, federationName, federationFile);

      // Join the federation
      joinFedEx(*rtiAmb, fedAmb, federateType, federationName);

      // Publish, subscribe and register and object
      if (!publishSubscribeAndRegisterObject(*rtiAmb))
      {
         resignAndDestroy(*rtiAmb, federationName);
         return 0;
      }

      // Publish and subscribe to the required interaction
      if (!publishAndSubscribeInteraction(*rtiAmb))
      {
         resignAndDestroy(*rtiAmb, federationName);
         return 0;
      }
      
      // Construct an attribute handle value pair set with the
      // values containing the attribute names
      AttributeHandleValueMap attrValues;

      for (DtAttrNameHandleMap::iterator iter = theAttrNameHandleMap.begin();
           iter != theAttrNameHandleMap.end();
           iter++)
      {
         // Attribute values will be just the name of the attribute.
         // Format is narrow string representation of attribute name to remain
         // compatible with 1.3 simple federate
         std::string temp = DtToString(iter->first);
         attrValues[iter->second].setData(temp.c_str(), temp.length()+1);
      }
   

      // Construct a parameter handle value pair set with the
      // values containing the parameter names
      ParameterHandleValueMap paramValues;

      for (DtParamNameHandleMap::iterator iter2 = theParamNameHandleMap.begin();
           iter2 != theParamNameHandleMap.end();
           iter2++)
      {
         // Parameter values will be just the name of the attribute.
         // Format is narrow string representation of parameter name to remain
         // compatible with 1.3 simple federate
         std::string temp = DtToString(iter2->first);
         paramValues[iter2->second].setData(temp.c_str(), temp.length()+1);
      }


#ifdef WIN32
      WSADATA data;
      WSAStartup(MAKEWORD(1,1), &data);
#endif
      rtiAmb->evokeCallback(0.0);
      long count=0;
      while (1)
      {
         std::stringstream ss;
         ss << "1516-" << count++;
         std::string tag(ss.str());
         // Tag format is narrow string representation compatible
         // with 1.3 simple federate

         // Update the object
         rtiAmb->updateAttributeValues(
            theObjectHandle,
            attrValues,
            VariableLengthData(tag.c_str(), tag.size()+1));

         // Send an interaction every few passes
         if ( count % 5 == 0 )
         {
            wcout << L"Sending interaction..." << endl;
            rtiAmb->sendInteraction(
               theInterClassHandle,
               paramValues,
               VariableLengthData(tag.c_str(), tag.size()+1));
         }

         rtiAmb->evokeMultipleCallbacks(0.1, 0.5);

         if (input.keybrdTick() < 0)
            break;
         
#ifdef WIN32
         Sleep(2000);
#else
         sleep(2);
#endif
      }

      // Resign and destroy federation
      resignAndDestroy(*rtiAmb, federationName);
   }
   catch (rti1516::Exception& ex)
   {
      wcout << L"RTI Exception (main loop): "
            << ex.what().c_str() << endl;
   }
#ifdef WIN32
   WSACleanup();
#endif

   return 0;

}

